import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:math' as math;
import 'package:camera/camera.dart';
import 'package:ffmpeg_kit_flutter_min_gpl/ffmpeg_kit.dart';
import 'package:ffmpeg_kit_flutter_min_gpl/ffmpeg_session.dart';
import 'package:ffmpeg_kit_flutter_min_gpl/return_code.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart';
import 'package:vibration/vibration.dart';
import 'package:video_player/video_player.dart';
// ignore: depend_on_referenced_packages
import 'package:path/path.dart' as p;

enum CameraMode {
  takePicture,
  record;
}

class CameraViewController {
  CameraViewController() : super();
  CameraController? _controller;
  bool _isRecording = false;
  bool _isControllerInited = false;
  bool get isRecording => _isRecording;
  XFile? targetFile;
  ResolutionPreset resolute = ResolutionPreset.high;
  CameraLensDirection _currentCamera = CameraLensDirection.front;
  bool _isCameraInitingNow = false;
  Future<void> startRecording() async {
    if (_isControllerInited && !_isRecording) {
      await _controller?.prepareForVideoRecording();
      await _controller?.startVideoRecording();

      _isRecording = true;
    }
  }

  Future<void> stopRecording() async {
    if (_isControllerInited && _isRecording) {
      targetFile = await _controller?.stopVideoRecording();
      _isRecording = false;

      await transportFile();
    }
  }

  Future<void> transportFile({bool isImage = false}) async {
    if (targetFile != null && _currentCamera == CameraLensDirection.front) {
      var saveFilePath = p.dirname(targetFile!.path);
      var path = p.join(
          saveFilePath,
          DateTime.now().millisecondsSinceEpoch.toString() +
              p.extension(targetFile!.path));
      late FFmpegSession fFmpegSession;
      if (Platform.isAndroid) {
        if (isImage) {
          fFmpegSession = await FFmpegKit.execute(
              '-y -i ${targetFile!.path}  -vf transpose=3 -preset ultrafast  $path ');
        } else {
          fFmpegSession = await FFmpegKit.execute(
              '-y -i ${targetFile!.path}  -vf hflip -preset ultrafast  $path ');
        }
      } else {
        if (isImage) {
          fFmpegSession = await FFmpegKit.execute(
              '-y -i ${targetFile!.path}  -vf  transpose=0 -preset ultrafast  $path ');
        } else {
          fFmpegSession = await FFmpegKit.execute(
              '-y -i ${targetFile!.path}   -preset ultrafast  $path ');
        }
      }

      var code = await fFmpegSession.getReturnCode();
      if (ReturnCode.isSuccess(code)) {
        log('ffmpeg covert success ');
        targetFile = XFile(path);
      } else {
        log('ffmpeg covert fail (${code?.toString()})');
      }
    }
  }

  Future<void> takePicture() async {
    if (_isControllerInited && !_isRecording) {
      targetFile = null;
      targetFile = await _controller?.takePicture();
      await transportFile(isImage: true);
    }
  }

  Future<void> initCameraWithDirection(CameraLensDirection direction) async {
    if (_isCameraInitingNow) {
      return;
    }
    _currentCamera = direction;
    _isControllerInited = false;

    _isCameraInitingNow = true;
    await _controller?.dispose();
    List<CameraDescription> cameras = await availableCameras();
    var result =
        cameras.firstWhere((element) => element.lensDirection == direction);
    _controller = CameraController(result, resolute,
        enableAudio: true, imageFormatGroup: ImageFormatGroup.jpeg);
    await _controller?.initialize();
    await _controller?.setFocusMode(FocusMode.auto);
    await _controller!.setFlashMode(FlashMode.auto);
    if (direction == CameraLensDirection.front) {}
    _isControllerInited = true;
    _isCameraInitingNow = false;
  }

  void dispose() {
    _controller?.dispose();
  }

  double getAspectOfCameraView() {
    if (resolute == ResolutionPreset.high) {
      return 1280.0 / 720;
    } else if (resolute == ResolutionPreset.medium) {
      return 720.0 / 480;
    } else if (resolute == ResolutionPreset.veryHigh) {
      return 1920.0 / 1080;
    } else if (resolute == ResolutionPreset.ultraHigh) {
      return 3840 / 2160;
    }
    return 4 / 3;
  }

  Future<void> toggleCamera() async {
    Vibration.vibrate(duration: 100);
    if (_currentCamera == CameraLensDirection.front) {
      _currentCamera = CameraLensDirection.back;
    } else {
      _currentCamera = CameraLensDirection.front;
    }
    await initCameraWithDirection(_currentCamera);
  }
}

class CameraView extends StatefulWidget {
  final CameraViewController? cameraController;
  final CameraMode mode;
  final int maxRecordSeconds;
  const CameraView(
      {Key? key,
      this.cameraController,
      this.mode = CameraMode.takePicture,
      this.maxRecordSeconds = 60})
      : super(key: key);

  @override
  State<CameraView> createState() => _CameraViewState();

  static Future<dynamic> showCameraView(BuildContext context,
      {CameraMode mode = CameraMode.takePicture, int maxRecordSeconds = 60}) {
    PageRoute pageRoute = PageRouteBuilder(pageBuilder: (BuildContext context,
        Animation<double> animation, Animation<double> secondaryAnimation) {
      return CameraView(
        mode: mode,
        maxRecordSeconds: maxRecordSeconds,
      );
    });
    return Navigator.of(context).push(pageRoute);
  }
}

class _CameraViewState extends State<CameraView>
    with WidgetsBindingObserver, SingleTickerProviderStateMixin {
  late Future<void> _initFuture;
  late CameraViewController _cameraController;

  ///是否拍照成功
  bool isActionSuccess = false;

  static double progressStart = 0;
  StreamController<double> pgrstreamController = StreamController();
  AnimationController? animationController;
  Animation<double>? timeAnimation;
  int startRecordingTime = 0;
  late Stream<double> updateStream;
  Tween<double> transportTween = Tween<double>(begin: 0, end: 2 * math.pi);
  @override
  void initState() {
    super.initState();
    _cameraController = widget.cameraController ?? CameraViewController();
    WidgetsBinding.instance.addObserver(this);
    _reset();
    _initFuture = _initCameraController();
    updateStream = pgrstreamController.stream.asBroadcastStream();
    if (widget.mode == CameraMode.record) {
      animationController = AnimationController(
        vsync: this,
        duration: Duration(
          seconds: widget.maxRecordSeconds,
        ),
        reverseDuration: const Duration(milliseconds: 800),
      );
      timeAnimation = Tween(begin: 0.0, end: widget.maxRecordSeconds * 1.0)
          .animate(animationController!);
      animationController!.addStatusListener((status) {
        if (status == AnimationStatus.dismissed) {
          _doStopRecording();
        } else if (status == AnimationStatus.completed) {
          animationController!.reverse().orCancel;
        }
      });
      animationController!.addListener(() {
        pgrstreamController.sink.add(timeAnimation!.value);
      });
    }
  }

  void _doStopRecording() async {
    if (widget.mode == CameraMode.record) {
      if (_cameraController.isRecording) {
        EasyLoading.show();
        await _cameraController.stopRecording();
        EasyLoading.dismiss();
      }
    }

    if (_cameraController.targetFile != null) {
      setState(() {
        isActionSuccess = true;
      });
    }
  }

  void _reset() {
    _cameraController._isRecording = false;
    _cameraController._isControllerInited = false;
  }

  ///初始化摄像头
  Future<void> _initCameraController() async {
    PermissionStatus permissionStatus = await Permission.camera.request();
    if (permissionStatus.isGranted) {
      await _cameraController.initCameraWithDirection(CameraLensDirection.back);

      if (mounted) {
        setState(() {});
      }
    } else {
      EasyLoading.showToast('请先授权访问摄像头');
    }
  }

  Future<void> _pause() async {
    try {
      if (_cameraController._isControllerInited) {
        if (_cameraController._isRecording) {
          await _cameraController._controller?.pauseVideoRecording();
          return;
        }
        await _cameraController._controller?.pausePreview();
      }
    } catch (e) {
      _reset();
      _cameraController._controller?.dispose();
    }
  }

  Future<void> _resume() async {
    if (!_cameraController._isControllerInited) {
      setState(() {
        _initFuture = _initCameraController();
      });
      return;
    }

    try {
      if (_cameraController._isControllerInited) {
        if (_cameraController._isRecording) {
          await _cameraController._controller?.resumeVideoRecording();
          return;
        }
        await _cameraController._controller?.resumePreview();
      }
    } catch (e) {
      _reset();
      _cameraController._controller?.dispose();

      setState(() {
        _initFuture = _initCameraController();
      });
    }
  }

  @override
  void dispose() {
    _cameraController._controller?.dispose();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
    _cameraController.dispose();
    animationController?.dispose();
    pgrstreamController.close();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);

    if (state == AppLifecycleState.paused ||
        state == AppLifecycleState.inactive) {
      _pause();
    } else if (state == AppLifecycleState.resumed) {
      _resume();
    }
  }

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => false,
      child: Container(
        color: Colors.black,
        child: FutureBuilder(
          builder: _buildContentWithFutureResult,
          future: _initFuture,
        ),
      ),
    );
  }

  Widget _buildContentWithFutureResult(
      BuildContext context, AsyncSnapshot snapshot) {
    bool showCameraView = snapshot.connectionState == ConnectionState.done &&
        _cameraController._isControllerInited;

    if (!showCameraView) return Container();

    double radio = _cameraController.getAspectOfCameraView();

    MediaQueryData mediaQuery = MediaQuery.of(context);

    return Stack(
      fit: StackFit.expand,
      children: [
        Positioned(
          top: 0,
          left: 0,
          right: 0,
          child: CameraPreview(
            _cameraController._controller!,
            child: isActionSuccess
                ? (widget.mode == CameraMode.takePicture
                    ? Image.file(File(_cameraController.targetFile!.path))
                    : _SimpleCirclePlayer(
                        path: _cameraController.targetFile!.path,
                      ))
                : null,
          ),
        ),
        if (widget.mode == CameraMode.record &&
            _cameraController.isRecording &&
            animationController?.status != AnimationStatus.reverse)
          Positioned(
            top: 0,
            left: 0,
            right: 0,
            child: SafeArea(
              child: StreamBuilder<double>(
                  stream: updateStream,
                  builder: (context, snapshot) {
                    return Text(
                      '${widget.maxRecordSeconds - (snapshot.data ?? 0 % 60).floor()}秒',
                      style: const TextStyle(color: Colors.white, fontSize: 17),
                      textAlign: TextAlign.center,
                    );
                  }),
            ),
          ),
        Positioned(
          bottom: 0,
          left: 0,
          right: 0,
          height: (mediaQuery.size.height - mediaQuery.size.width * radio)
              .clamp(150, 300),
          child: Container(
            color: Colors.black.withOpacity(0.4),
            child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  IconButton(
                    iconSize: 40,
                    onPressed: () {
                      if (isActionSuccess) {
                        File(_cameraController.targetFile!.path).delete();
                        _cameraController._controller?.resumePreview();
                        setState(() {
                          isActionSuccess = false;
                        });
                      } else {
                        if (!mounted) return;
                        Navigator.pop(context);
                      }
                    },
                    icon: const Icon(
                      Icons.close_rounded,
                      color: Colors.white,
                    ),
                  ),
                  AnimatedOpacity(
                    opacity: isActionSuccess ? 0 : 1,
                    duration: const Duration(milliseconds: 200),
                    child: IgnorePointer(
                      ignoring: isActionSuccess,
                      child: StreamBuilder<double>(
                          stream: updateStream,
                          builder: (context, snapshot) {
                            return Stack(
                              children: [
                                _CameraCenterButton(
                                  onTap: widget.mode == CameraMode.takePicture
                                      ? doTakePicture
                                      : doRecordAction,
                                  progress: animationController != null
                                      ? transportTween
                                          .evaluate(animationController!)
                                      : 0,
                                  startProgress: progressStart,
                                ),
                                if (widget.mode == CameraMode.record)
                                  Positioned(
                                    top: 0,
                                    bottom: 0,
                                    left: 0,
                                    right: 0,
                                    child: IgnorePointer(
                                      ignoring: true,
                                      child: Icon(
                                        _cameraController.isRecording
                                            ? Icons.stop
                                            : Icons.play_arrow,
                                        color: Colors.white,
                                      ),
                                    ),
                                  ),
                              ],
                            );
                          }),
                    ),
                  ),
                  AnimatedCrossFade(
                    firstChild: IconButton(
                      iconSize: 40,
                      onPressed: () async {
                        AssetEntity? result;
                        EasyLoading.show();
                        if (_cameraController.targetFile != null) {
                          if (widget.mode == CameraMode.takePicture) {
                            result =
                                await PhotoManager.editor.saveImageWithPath(
                              _cameraController.targetFile!.path,
                              title: _cameraController.targetFile!.name,
                            );
                          } else {
                            result = await PhotoManager.editor.saveVideo(
                              File(_cameraController.targetFile!.path),
                              title: _cameraController.targetFile!.name,
                            );
                          }
                        }
                        EasyLoading.dismiss();
                        if (!mounted) return;
                        Navigator.pop(context, result);
                      },
                      icon: const Icon(
                        Icons.check_rounded,
                        color: Colors.white,
                      ),
                    ),
                    duration: const Duration(milliseconds: 200),
                    secondChild: IconButton(
                      iconSize: 40,
                      icon: const Icon(
                        Icons.cameraswitch,
                        color: Colors.white,
                      ),
                      onPressed: () async {
                        await _cameraController.toggleCamera();
                        setState(() {});
                      },
                    ),
                    crossFadeState: isActionSuccess
                        ? CrossFadeState.showFirst
                        : CrossFadeState.showSecond,
                  ),
                ]),
          ),
        )
      ],
    );
  }

  ///拍照成功后,给用户选择是否使用当前照片
  Future<void> doTakePicture() async {
    Vibration.vibrate(duration: 100);
    await _cameraController.takePicture();
    await _cameraController._controller?.pausePreview();
    if (_cameraController.targetFile != null) {
      setState(() {
        isActionSuccess = true;
      });
    }
  }

  Future<void> doRecordAction() async {
    if (widget.mode == CameraMode.record) {
      if (!_cameraController.isRecording) {
        Vibration.vibrate(duration: 100);
        await _cameraController.startRecording();
        startRecordingTime = DateTime.now().millisecondsSinceEpoch;
        if (_cameraController.isRecording) {
          setState(() {
            animationController!.forward().orCancel;
          });
        }
      } else {
        if (DateTime.now().millisecondsSinceEpoch - startRecordingTime >
            1 * 1000) {
          if (animationController!.isAnimating) {
            Vibration.vibrate(duration: 100);
            animationController!.stop();

            setState(() {
              animationController!.reverse().orCancel;
            });
          }
        }
      }
    }
  }
}

class _CameraCenterButton extends StatefulWidget {
  final VoidCallback? onTap;
  final double? progress;
  final double? startProgress;

  const _CameraCenterButton(
      // ignore: unused_element
      {super.key,
      this.onTap,
      this.progress,
      this.startProgress});
  @override
  State<StatefulWidget> createState() {
    return _CameraCenterButtonState();
  }
}

class _CameraCenterButtonState extends State<_CameraCenterButton>
    with SingleTickerProviderStateMixin {
  late AnimationController animationController;
  late Animation<double> _scaleAnimation;

  @override
  void initState() {
    super.initState();
    animationController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 200),
    );
    final Animation<double> curve = CurvedAnimation(
      parent: animationController,
      curve: Curves.easeIn,
      reverseCurve: Curves.easeOut,
    );
    _scaleAnimation = Tween(begin: 0.0, end: 10.0).animate(curve);

    animationController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        animationController.reverse();
      } else if (status == AnimationStatus.dismissed) {
        widget.onTap?.call();
      }
    });
    animationController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    animationController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return SizedOverflowBox(
      size: const Size(70, 70),
      child: GestureDetector(
        behavior: HitTestBehavior.opaque,
        onTap: () {
          animationController.forward().orCancel;
        },
        child: Transform.rotate(
          angle: math.pi,
          child: CustomPaint(
            foregroundPainter: CirclePainter(
              progress: widget.progress ?? 0,
              startProgress: widget.startProgress ?? 0,
            ),
            child: Container(
              width: 70 + _scaleAnimation.value,
              height: 70 + _scaleAnimation.value,
              decoration: BoxDecoration(
                borderRadius: BorderRadius.circular(70),
                color: Colors.white,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class CirclePainter extends CustomPainter {
  final double progress;
  final double startProgress;

  CirclePainter({this.progress = 0, this.startProgress = 0});
  @override
  void paint(Canvas canvas, Size size) {
    final strokeWidth = size.width / 12.0;
    final center = Offset(size.width / 2, size.height / 2);
    final radius = (size.width - strokeWidth) / 2;
    var paint1 = Paint()
      ..color = Colors.green
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.fill;
    //a circle
    if (startProgress != progress) {
      canvas.drawArc(
          Rect.fromCenter(
              center: center, width: size.width, height: size.height),
          startProgress,
          progress,
          true,
          paint1);
    }

    paint1.color = Colors.red;
    paint1.style = PaintingStyle.fill;
    canvas.drawCircle(center, radius, paint1);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) =>
      (oldDelegate as CirclePainter).progress != progress;
}

class _SimpleCirclePlayer extends StatefulWidget {
  final String path;
  const _SimpleCirclePlayer({Key? key, required this.path}) : super(key: key);

  @override
  State<_SimpleCirclePlayer> createState() => __SimpleCirclePlayerState();
}

class __SimpleCirclePlayerState extends State<_SimpleCirclePlayer> {
  late VideoPlayerController _controller;

  @override
  void initState() {
    super.initState();
    _controller = VideoPlayerController.file(File(widget.path));
    _controller.setLooping(true);
    _controller.initialize().then((value) {
      if (mounted) {
        _controller.play();
        setState(() {});
      }
    });
  }

  @override
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return VideoPlayer(_controller);
  }
}
